This notebook compares a generated template snap roll to one recorded with a 2m F3A plane using Ardupilot.
from flightanalysis import Section, Line, Snap
from geometry import Transformation, Point
from flightplotting.plots import plotsec
import numpy as np
from geometry import Quaternion
import plotly
plotly.offline.init_notebook_mode()
nb_layout = dict(width=800,
height=300,
margin=dict(l=5, r=5, t=5, b=1),
legend=dict(yanchor="top", xanchor="left", x=0.8, y=0.99)
)
The template snap is generated using the Snap principal element. Below short sections of line are also appended before and after the snap for easier comparison to the template section.
line_before = Line(2).create_template(Transformation(rotation=Quaternion.from_euler(Point(np.pi, 0, 0))), 30)
snap = Snap(1, rate=1700).scale(170).create_template(line_before[-1].transform, 30)
line_after = Line(2).create_template(snap[-1].transform, 30)
snap = Section.stack([line_before, snap, line_after])
plotsec(snap, nmodels = 7, scale=2)
Some real snaps were recorded using Ardupilot and saved as a section csv. The sections are transformed so that the sections x axis points along the line the snap is on and the y axis is parallel to wings at the start of the section. This is done in the local_section function. The snaps are found by selecting all the discrete regions where the roll rate is above a threshold, then extending forwards and backwards a little.
snap_sec = Section.from_csv("examples/snap_roll_section.csv")
from scipy.cluster.vq import kmeans
from scipy.optimize import minimize
from geometry import Transformation, Coord
def local_section(base_sec):
first_state = base_sec.get_state_from_index(0)
last_state = base_sec.get_state_from_index(-1)
wing_axis = first_state.att.transform_point(Point(0,-1,0))
snap_coord = Coord.from_xy(first_state.pos, last_state.pos - first_state.pos, wing_axis)
transform = Transformation.from_coords(snap_coord, Coord.from_nothing())
temp = base_sec.transform(transform)
temp = temp.transform(Transformation(-temp.get_state_from_index(0).pos))
temp.data.index = temp.data.index - temp.data.index[0]
return temp
ssec = snap_sec.subset(480, -1)
brv_thresh = 7
temp_sec = ssec.data.copy()
snaps = []
while temp_sec.brvr.abs().max() > brv_thresh:
snap_start = temp_sec.loc[temp_sec.brvr.abs()>brv_thresh].iloc[0].name
temp_sec = temp_sec.loc[snap_start:]
snap_end = temp_sec.loc[temp_sec.brvr.abs()<brv_thresh].iloc[0].name
temp_sec = temp_sec.loc[snap_end:]
snaps.append(local_section(ssec.subset(snap_start - 0.3, snap_end + 0.3)))
print("{} snaps found".format(len(snaps)))
#fast_rolls.describe()
20 snaps found
The figures below compare 3D visualisations of the constructed snap to one of the flown snaps
def plotsnap(sec):
fig = plotsec(sec, nmodels=10, scale=2, show_axes = True, color="grey")
fig.update_layout(width=1200, height=300, margin=dict(l=0, r=0, t=0, b=0))
return fig
plotsnap(snap).show()
plotsnap(snaps[2]).show()
The graph below show control inputs and axis rates for one of the flown snaps. It shows the initial elevator input to create the pitch break, then the rudder and aileron coming in together and the elevator dropping back a little for the autorotation. The axis rates roughly follow the control inputs.
from flightplotting.plots import control_brv_plot
control_brv_plot(snaps[2]).update_layout(width=1000).show()
The graphs below compare axis rates, alpha and beta between one of the recorded snaps and the template.
from flightplotting.plots import aoa_brv_plot
aoa_brv_plot(snaps[2]).update_layout(width=800,height=300).show()
aoa_brv_plot(snap).update_layout(width=800,height=300).show()